package com.panopset.io;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;

import com.panopset.Alert;
import com.panopset.Logger;
import com.panopset.compat.Strings;

/**
 * Short file operations, not practical for files larger than available memory.
 * 
 * @author Karl Dinwiddie
 * 
 */
public final class ShortFile {

	/**
	 * @param path
	 *            Path to file.
	 * @return Text in file.
	 */
	public String getFileText(final String path) {
		return getFileText(new File(path));
	}

	/**
	 * Return contents of a file as a String.
	 * 
	 * @param file
	 *            File.
	 * @return String full text of file.
	 */
	public String getFileText(final File file) {
		return getFileText(file, null);
	}

	/**
	 * Return contents of a file as a String.
	 * 
	 * @param file
	 *            File.
	 * @param textFilter
	 *            Text filter.
	 * @return String full text of file.
	 */
	public String getFileText(final File file, final TextFilter textFilter) {
		StringWriter rtn = new StringWriter();
		boolean firstTime = true;
		if (!file.exists()) {
			return "";
		}
		for (String s : loadFile(file)) {
			if (firstTime) {
				firstTime = false;
			} else {
				rtn.append(Strings.getEol());
			}
			if (textFilter == null) {
				rtn.append(s);
			} else {
				rtn.append(textFilter.filter(s));
			}
		}
		return rtn.toString();
	}

	/**
	 * Load a file into an array. This should be done with reasonable sized text
	 * files.
	 * 
	 * @param file
	 *            File to load.
	 * @return array Array to load file in to.
	 */
	public static List<String> loadFile(final File file) {
		final List<String> lines = new ArrayList<String>();
		if (file == null) {
			return lines;
		}
		if (!file.exists()) {
			return lines;
		}
		new FileProcessor.Builder()
				.fileAndListener(file, new FileProcessor.Listener() {

					@Override
					public void finish(final FileProcessor processor) {
						// nothing to do
					}

					@Override
					public void process(final FileProcessor processor,
							final String[] s) {
						lines.add(s[0]);
					}
				}).construct().exec();
		return lines;
	}

	/**
	 * Save String to file.
	 * 
	 * @param str
	 *            String to save.
	 * @param append
	 *            True if appending to end of file, false if replacing file.
	 * @param file
	 *            File to save String to.
	 * @return true iff successful
	 */
	public boolean saveStringToFile(final String str, final File file,
			final boolean append) {
		try {
			new Filez().createParentDirectories(file);
			FileWriter fw = new FileWriter(file, append);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(str);
			bw.close();
			fw.close();
			return true;
		} catch (IOException ex) {
			Logger.log(ex);
			return false;
		}
	}

	/**
	 * Save String to file.
	 * 
	 * @param str
	 *            String to save.
	 * @param file
	 *            File to save String to.
	 * @return true iff successful
	 */
	public boolean saveStringToFile(final String str, final File file) {
		return saveStringToFile(str, file, false);
	}

	/**
	 * This method will return true if the files are identical, ignoring the
	 * kind of return character used.
	 * 
	 * @param one
	 *            First file to compare.
	 * @param two
	 *            Second file to compare.
	 * @return true Only if both files exist and are identical.
	 */
	public boolean filesAreSame(final File one, final File two) {
		final boolean[] rtn = new boolean[1];
		rtn[0] = false;
		new FileProcessor.Builder().file(one).file(two)
				.listener(new FileProcessor.Listener() {

					@Override
					public void finish(final FileProcessor processor) {
						rtn[0] = true;
						processor.stop();
					}

					@Override
					public void process(final FileProcessor processor,
							final String[] s) {
						if (s.length != 2 || s[0] == null || s[1] == null
								|| !s[0].equals(s[1])) {
							processor.stop();
							return;
						}
					}
				}).construct().exec();
		return rtn[0];
	}

	/**
	 * Override the filter method to provide a way to edit lines as they are
	 * read.
	 */
	public abstract class TextFilter {
		/**
		 * Apply filter.
		 * 
		 * @param s
		 *            String to filter.
		 * @return Filtered value.
		 */
		public abstract String filter(final String s);
	}

	public List<String> readLines(File file) {
		try {
			if (!file.exists()) {
				Alert.dspmsg(new Filez().getCanonicalPath(file)
						+ " does not exist.");
			}
			return FileUtils.readLines(file);
		} catch (IOException e) {
			return new ArrayList<String>();
		}
	}

}
